package edu.upenn.seas.peerreview.smui
import edu.upenn.seas.peerreview.{Id, Sink}

/**
 * The base application ("UI") component. It sends and receives messages from
 * the state machine (but could also send/receive to other machines in the game.
 */
class DecisionAdapter(smId: Id, dec : Decider, myId : Id, transport : Sink.T) 
  extends BSHandler(myId, transport) {
  
  /** Return my Id. */
  def getId() = myId
 
  var opponents : List[Id] = List()
  var liveOpponents : List[Id] = List()
  var boardSize : Point = Point(0, 0)
  var shipSet : List[Int] = List()
  val boards : scala.collection.mutable.Map[Id, Board] = scala.collection.mutable.Map[Id,Board]()
  
  // Call onRegister and pass it this
  dec.onRegister(myBoard, getBoard)
  send(smId, IAmYourUI())

  
  /** Ask Decider to place ships, then send them. */
  def placePieces() : Unit = {
    dec.placeShips(boardSize, shipSet).foreach(tup => {
      sendMsg(PlaceShip(tup._1, tup._2))
      val v = Vessel((tup._1.x +tup._1.y - tup._2.x - tup._2.y).abs + 1) // Calculate size of ship
      for(x <- tup._1.x to tup._2.x; 
          y <- tup._1.y to tup._2.y)
          boards(smId) = boards(smId) + (Point(x,y), CellStatus.Ship(v))
    })
  }
  
  /** Ask decider for a move. */
  private def makeMove() = {
    Debug.debug("nrpc")("[DA for %s] %s".format(smId, liveOpponents))
    val move = dec.makeMove(liveOpponents)
    sendMsg(FireAt(move._2, move._1))
  }
  
  /** Send a message to my state machine. */
  private def sendMsg(b : Payload) = {
    assert(smId != null)
    send(smId, b)
  }
  
  /** Return this player's board. */
  private def myBoard() : Board             = boards(smId)
  
  /** Return an opponent player's board. */
  private def getBoard(person : Id) : Board = boards(person)
  
  /** Internally update the game boards' states. */
  private def updateBoards(opp:Id, locOnBoard:Point, result:FireResultType) { 
    def toCellStatus(f:FireResultType) : CellStatus = f match {
        case Hit | Sunk | Defeat | RedundantHit => CellStatus.Hit
        case x => CellStatus.Miss
    }
    boards(opp) = boards(opp) + (locOnBoard, toCellStatus(result))
  }
    
  /** Handle an incoming message. */
  def handle (e: Envelope) : Unit = { 
    e.contents match {
      case NewGameConfigs(opp, board, ships) => { 
        opponents = opp
        liveOpponents = opponents
        boardSize = board
        shipSet = ships
        boards.clear
        (smId :: opp).foreach(o => boards += (o->new BoardImpl(boardSize.x, boardSize.y)))
        dec.onNewGame(opponents, boardSize)
        placePieces()
      }
      case RequestAction() => { 
        makeMove();
      }
      
      case FireResult(Envelope(from, to, FireAt(loc, target)), result) => {
        updateBoards(target, loc, result)
        if (from == smId) // Report of your firing.
          dec.onYourFireReport(to, loc, result)
        else if (target == smId) // Report of a fire at you.
          dec.onFiredAtReport(from, loc, result)
        else // You are not involved in this fire result.
          dec.onOpponentFireReport(from, to, loc, result)
        if (result == Defeat)
          liveOpponents -= to
      }
      
      case Forward(from, msg) => msg match {
        case MoveMade() => dec.onOpponentMoved(from)
        case FireAt(point, target) => dec.onBeingFiredAt(from, point)
        case PlayerResult(h,s,k,w) => {
          if (from == smId) {
            dec.onYourFinish(h,s,k,w)
          } else
            dec.onPlayerResult(from, h,s,k,w)
        }
      }

      case GameOver() => dec.onGameOver()
      case TournamentOver() => dec.onTournamentOver()
      case _ => 
    }
  }
}
